package com.wens.baselibrary.util.crash

import android.os.Process
import com.blankj.utilcode.util.ThreadUtils
import com.blankj.utilcode.util.Utils
import java.io.File
import java.io.FileOutputStream
import kotlin.system.exitProcess

/**
 * @author Stay
 * 在Application中统一捕获异常，保存到文件中下次再打开时上传
 */
class CrashHandler
/**
 * 保证只有一个CrashHandler实例
 */
private constructor() : Thread.UncaughtExceptionHandler {
    /**
     * 系统默认的UncaughtException处理类
     */
    private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null

    /**
     * 初始化,注册Context对象,
     * 获取系统默认的UncaughtException处理器,
     * 设置该CrashHandler为程序的默认处理器
     */
    init {
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    override fun uncaughtException(thread: Thread, ex: Throwable) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler!!.uncaughtException(thread, ex)
        } else {  //如果自己处理了异常，则不会弹出错误对话框，则需要手动退出app
            try {
                Thread.sleep(3000)
            } catch (ignored: InterruptedException) {
            }
            Process.killProcess(Process.myPid())
            exitProcess(10)
        }
    }

    /**
     * 自定义错误处理,收集错误信息
     * 发送错误报告等操作均在此完成.
     * 开发者可以根据自己的情况来自定义异常处理逻辑
     *
     * @return true代表处理该异常，不再向上抛异常，
     * false代表不处理该异常(可以将该log信息存储起来)然后交给上层(这里就到了系统的异常处理)去处理，
     * 简单来说就是true不会弹出那个错误提示框，false就会弹出
     */
    private fun handleException(ex: Throwable?): Boolean {
        if (ex == null) {
            return false
        }
        val stack = ex.stackTrace
        val message = ex.message
        //使用Toast来显示异常信息
        ThreadUtils.executeByCpu(object : ThreadUtils.SimpleTask<Any>() {

            override fun doInBackground(): Any {
                //            Toast.makeText(BaseSdk.context, "程序出错啦:" + message, Toast.LENGTH_LONG).show();
//                可以只创建一个文件，以后全部往里面append然后发送，这样就会有重复的信息，个人不推荐
                val fileName = "crash-" + System.currentTimeMillis() + ".log"
                val file =
                    File(Utils.getApp().getExternalFilesDir("crash"), fileName)
                try {
                    val fos = FileOutputStream(file, true)
                    if (message != null) {
                        fos.write(message.toByteArray())
                    }
                    for (stackTraceElement in stack) {
                        fos.write(stackTraceElement.toString().toByteArray())
                    }
                    fos.flush()
                    fos.close()
                } catch (ignored: Exception) {
                }
                return file
            }

            override fun onSuccess(result: Any?) {

            }
        })
        return false
    }

    companion object {
        /**
         * 是否开启日志输出,在Debug状态下开启,
         * 在Release状态下关闭以提示程序性能
         */
        const val DEBUG = true

        /**
         * CrashHandler实例
         */
        private var INSTANCE: CrashHandler? = null

        /**
         * 获取CrashHandler实例 ,单例模式
         */
        val instance: CrashHandler
            get() {
                if (INSTANCE == null) {
                    INSTANCE = CrashHandler()
                }
                return INSTANCE!!
            }
    }
}